package com.duniptech.engine.core.modeling;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

import com.duniptech.engine.core.modeling.api.IAtomic;
import com.duniptech.engine.core.modeling.api.ICoupled;
import com.duniptech.engine.core.modeling.api.ICoupling;
import com.duniptech.engine.core.modeling.api.IMessage;
import com.duniptech.engine.core.simulation.api.ICoordinator;



/**
 * Root class used to define coupled models. 
 * 
 * @author Jos� Luis Risco Mart�n.
 */
public class Coupled extends Atomic implements ICoupled {
	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	// Comment
	private Set<IAtomic> components;
	private ArrayList<ICoupling> ic;
	private ArrayList<ICoupling> eic;
	private ArrayList<ICoupling> eoc;

	private ICoordinator coordinator;
	
	/** Constructor. */
	public Coupled(String name) { 
		super(name);
		components = new HashSet<IAtomic>();
		ic = new ArrayList<ICoupling>();
		eic = new ArrayList<ICoupling>();
		eoc = new ArrayList<ICoupling>();
	}
	// ---------------------------------------------------------------
	// DEVS PROTOCOL
	// ---------------------------------------------------------------
	public void deltint() { }
	public void deltext(double e, IMessage x) {	}
	public void deltcon(double e, IMessage x) {	
		deltint();
		deltext(0,x);
	}
	public IMessage lambda() { 
		return null;
	}

	public IAtomic getComponentWithName(String name) { 
		Iterator<IAtomic> it = components.iterator();
		while(it.hasNext()){
			IAtomic model = (IAtomic)it.next();
			if(model.getName().equalsIgnoreCase(name)){
				return model;
			}
		}
		
		return null; 
	}

	public void addComponent(IAtomic iAtomic) {
		iAtomic.setParent(this);
		components.add(iAtomic); 
	}
	
	public void removeComponent(IAtomic iAtomic){
		removeAllCouplingFor(iAtomic);
		components.remove(iAtomic);
	}
	
	public void addCoupling(IAtomic componentFrom, String portFrom, IAtomic componentTo, String portTo) {
		ICoupling iCoupling = new Coupling(componentFrom, portFrom, componentTo, portTo);
		// Add to connections
		if(componentFrom==this) {
			if(componentFrom.containsInPort(portFrom) && componentTo.containsInPort(portTo))
				eic.add(iCoupling);
		}
		else if(componentTo==this) {
			if(componentFrom.containsOutPort(portFrom) && componentTo.containsOutPort(portTo))
				eoc.add(iCoupling);
		}
		else {
			if(componentFrom.containsOutPort(portFrom) && componentTo.containsInPort(portTo))
				ic.add(iCoupling);
		}
	}
	
	public void removeAllCouplingFor(IAtomic component){
		ArrayList<ICoupling> toRemove = new ArrayList<ICoupling>();
		for(int i=0; i<eic.size(); i++){
			ICoupling c = (ICoupling)eic.get(i);
			if(c.getComponentFrom().getName().equals(component.getName()) || 
					c.getComponentTo().getName().equals(component.getName())){
				toRemove.add(c);
			}
		}
		eic.removeAll(toRemove);
		toRemove.clear();
		
		for(int i=0; i<eoc.size(); i++){
			ICoupling c = (ICoupling)eoc.get(i);
			if(c.getComponentFrom().getName().equals(component.getName()) || 
					c.getComponentTo().getName().equals(component.getName())){
				toRemove.add(c);
			}
		}
		eoc.removeAll(toRemove);
		toRemove.clear();
		
		for(int i=0; i<ic.size(); i++){
			ICoupling c = (ICoupling)ic.get(i);
			if(c.getComponentFrom().getName().equals(component.getName()) || 
					c.getComponentTo().getName().equals(component.getName())){
				toRemove.add(c);
			}
		}
		ic.removeAll(toRemove);	
	}
	
	public void removeCoupling(IAtomic componentFrom, String portFrom, IAtomic componentTo, String portTo){
		ICoupling iCoupling = new Coupling(componentFrom, portFrom, componentTo, portTo);
		if(componentFrom==this){
			if(eic.contains(iCoupling))
				eic.remove(iCoupling);
		}
		else if(componentTo == this){
			if(eoc.contains(iCoupling))
				eoc.remove(iCoupling);
		}
		else
			if(ic.contains(iCoupling))
				ic.remove(iCoupling);
	}
	
	public Collection<IAtomic> getComponents() { return components; }
	
	public Collection<ICoupling> getEICs() { return eic; }
	
	public Collection<ICoupling> getICs() { return ic; }
	
	public Collection<ICoupling> getEOCs() { return eoc; }
	
	public ICoordinator getCoordinator() {
		return coordinator;
	}
	public void setCoordinator(ICoordinator coord) {
		this.coordinator = coord;
	}
	
	public String toString(){
		StringBuffer sb = new StringBuffer();
		sb.append(getName()+"[");
		for(IAtomic atomic: components){
			if(atomic instanceof ICoupled){
				sb.append("{"+atomic+"}");
			}
			else{
				sb.append(atomic.getName()+" ");
			}
		}
		sb.append("]");
		return sb.toString();
	}
	
	public void logState(){
		StringBuffer sb = new StringBuffer();
		for(IAtomic model: components){
			model.logState();
		}
		System.out.println(sb.toString());
	}
}
